Mientras estudiaba el delegado, que en realidad es una clase abstracta en Delegate.cs , vi el siguiente método que no entiendo
? aunque ya es un tipo de referencia ( clase )?[]? significado en el parámetro¿Podrías explicar?
public static Delegate? Combine(params Delegate?[]? delegates) { if (delegates == null || delegates.Length == 0) return null; Delegate? d = delegates[0]; for (int i = 1; i < delegates.Length; i++) d = Combine(d, delegates[i]); return d; } params Delegate?[] delegates : es una matriz de Delegate anulables
params Delegate?[]? delegates : toda la matriz puede ser anulable
Dado que cada parámetro es del tipo Delegate? y devuelves un índice del Delegate?[]? matriz, entonces tiene sentido que el tipo de valor devuelto sea Delegate? de lo contrario, el compilador devolvería un error como si estuviera regresando e int de un método que devuelve una cadena.
Podría cambiar, por ejemplo, su código para devolver un tipo de Delegate como este:
public static Delegate Combine(params Delegate?[]? delegates) { Delegate defaulDelegate = // someDelegate here if (delegates == null || delegates.Length == 0) return defaulDelegate; Delegate d = delegates[0] ?? defaulDelegate; for (int i = 1; i < delegates.Length; i++) d = Combine(d, delegates[i]); return d; }Los tipos de referencia anulables son nuevos en C# 8.0, no existían antes.
Es una cuestión de documentación y cómo se producen las advertencias en tiempo de compilación.
La excepción "objeto no establecido en una instancia de un objeto" es bastante común. Pero esta es una excepción en tiempo de ejecución, ya se puede descubrir parcialmente en tiempo de compilación.
Para un Delegate d siempre puedes llamar
d.Invoke();es decir, puede codificarlo, en tiempo de compilación no sucederá nada. Puede generar excepciones en tiempo de ejecución.
Mientras que para un nuevo Delegate? p este código
p.Invoke(); producirá una advertencia del compilador. CS8602: Dereference of a possibly null reference menos que escriba:
p?.Invoke();lo que significa, llamar solo si no es nulo.
Por lo que documenta una variable puede contener nulo o no. Genera advertencias antes y puede evitar múltiples pruebas de nulo. Lo mismo que tienes para int e int?. Está seguro de que uno no es nulo, y sabe cómo convertir uno en el otro.
En C# 8, se deben marcar explícitamente los tipos de referencia como anulables.
De forma predeterminada, esos tipos no pueden contener valores nulos, algo similar a los tipos de valor. Si bien esto no cambia la forma en que funcionan las cosas bajo el capó, el verificador de tipos requerirá que lo haga manualmente.
El código dado se refactoriza para que funcione con C# 8, pero no se beneficia de esta nueva función.
public static Delegate? Combine(params Delegate?[]? delegates) { // ...[]? delegates - is not null-safe, so check for null and emptiness if (delegates == null || delegates.Length == 0) return null; // Delegate? d - is not null-safe too Delegate? d = delegates[0]; for (int i = 1; i < delegates.Length; i++) d = Combine(d, delegates[i]); return d; }Aquí hay un ejemplo de un código actualizado (que no funciona, solo una idea) que aprovecha esta función. Nos salvó de una verificación nula y simplificó un poco este método.
public static Delegate? Combine(params Delegate[] delegates) { // `...[] delegates` - is null-safe, so just check if array is empty if (delegates.Length == 0) return null; // `d` - is null-safe too, since we know for sure `delegates` is both not null and not empty Delegate d = delegates[0]; for (int i = 1; i < delegates.Length; i++) // then here is a problem if `Combine` returns nullable // probably, we can add some null-checks here OR mark `d` as nullable d = Combine(d, delegates[i]); return d; }